<?php
// @formatter:off
/**
 * @file WCLog.php
 * @author Alejandro Dario Simi
 * @date $Date: 2013-07-11 21:02:48 +0000 (Thu, 11 Jul 2013) $
 *
 * $Id: WCLog.php 95 2013-07-11 21:02:48Z daemonraco@gmail.com $
 * $URL: http://wcomix.googlecode.com/svn/tags/wcomix-1.0.0.2/includes/WCLog.php $
 */

/**
 * @class WCLogLevel
 * This is an enumerative definition of log message level of importance. Lower
 * the value, higher the importance of a log message
 */
class WCLogLevel {
	/**
	 * This kind of messages is for errors that can not be neither avoided
	 * nor recovered from.
	 */
	const Fatal		= 0;
	/**
	 * This kind of messages is for errors that may be avoided.
	 */
	const Error		= 1;
	/**
	 * This kind of messages is possible errors of thing that are not
	 * completely okey.
	 */
	const Warning		= 2;
	/**
	 * This kind of messages is for informative messages.
	 */
	const Info		= 3;
	/**
	 * This kind of messages is for non essential messages and things the
	 * final user may not need.
	 */
	const Debug		= 4;
	/**
	 * This kind of messages is for completely unnessary messages for final
	 * user, yet useful for developers.
	 */
	const DetailledDebug	= 5;
};
// @formatter:on

/**
 * @class WCLog This singleton class holds the logic to manage logs and files
 * related to them.
 */
class WCLog {
	/**
	 * @var WCLog[string] This is the list of logs being managed by the
	 * singleton.
	 */
	protected static $_Logs = array();
	/**
	 * @var string Log's name.
	 */
	protected $_name = false;
	/**
	 * @var string Full system path for log's file.
	 */
	protected $_path = false;
	/**
	 * @var boolean This flag indicates that current log has been set.
	 */
	protected $_ok = false;
	/**
	 * @var int This property holds the current log level to be shown. It must
	 * be a value from class WCLogLevel.
	 */
	protected $_level = WCLogLevel::Info;
	/**
	 * @var string This is the name of the method in charge of writing on
	 * files.
	 */
	protected $_writeOnFile = false;
	/**
	 * @var string This is the name of the method in charge of writing on
	 * the screen.
	 */
	protected $_writeOnScreen = false;
	/**
	 * Class constructor.
	 *
	 * @param string id ID to associate with the log.
	 */
	protected function __construct($id) {
		//
		// Loading global pointers.
		global $wcPaths;
		//
		// Assigning the id as the logs name.
		$this->_name = $id;
		//
		// Building the full system path for log's file.
		$this->_path = WCSanitizer::SystemPath("{$wcPaths['logs-directory']}/wc_{$id}_".date('YmdHis').".log");
		//
		// Checking if the current user has writing privileges on logs
		// directory. Otherwise, a error is triggered
		if(!is_writable($wcPaths["logs-directory"])) {
			trigger_error(__CLASS__."::".__FUNCTION__.": Logs directory has no read/write permissions.", E_USER_ERROR);
		}
		//
		// Adding the log's header.
		$this->header();
	}
	/**
	 * Class destructor.
	 */
	public function __destruct() {
		//
		// Before the log closes, the footer must be added.
		$this->footer();
	}
	//
	// Public methods.
	/**
	 * This method allows to know if the current log is ready to be used or
	 * not. In other words, if it has been set.
	 *
	 * @return boolean Returns true when it's ready to be used.
	 */
	public function isOk() {
		return $this->_ok;
	}
	/**
	 * This method allows to get current log's level.
	 */
	public function level() {
		return $this->_level;
	}
	/**
	 * This method allows to get current log's name.
	 */
	public function name() {
		return $this->_name;
	}
	/**
	 * This method allows to get the full system full path of the log file.
	 *
	 * @return string Returns the full path of the log file
	 */
	public function path() {
		return $this->_path;
	}
	/**
	 * This method allows to set the current log and leave it ready to be
	 * used.
	 *
	 * @param int $level Level of messages to be shown. When absent,
	 * WCLogLevel::Info is assumed.
	 * @param boolean $writeOnFile Indicates if writings on files are
	 * activated or not. When absent, true is assumed.
	 * @param boolean $writeOnScreen Indicates if writings on the screen are
	 * activated or not. When absent, true is assumed.
	 */
	public function set($level = WCLogLevel::Info, $writeOnFile = true, $writeOnScreen = true) {
		//
		// Loading global pointers.
		global $wcDefaults;
		//
		// Setting the method in charge of file writings.
		$this->_writeOnFile = !$wcDefaults["no-logs"] && $writeOnFile ? "realWriteOnFile" : "dummy";
		//
		// Setting the method in charge of screen writings.
		$this->_writeOnScreen = $writeOnScreen ? "realWriteOnScreen" : "dummy";
		//
		// Setting the level of log messages to be shown,
		$this->_level = $level;
		//
		// Setting the log as ready to be used.
		$this->_ok = true;
		//
		// Returning the status of the log.
		return $this->isOk();
	}
	/**
	 * This method writes a message on the log.
	 *
	 * @param string $log Message to write in the log.
	 * @param int $level Log level for the message to show. When absent,
	 * WCLogLevel::Info is assumed.
	 */
	public function write($log, $level = WCLogLevel::Info) {
		//
		// Checking if the log is ready to be used.
		if($this->isOk()) {
			//
			// Checking if the level of the message is allowed to be
			// written.
			if($level <= $this->level()) {
				//
				// Triggering screen and file writings.
				eval("\$this->{$this->_writeOnScreen}(\$log, \$level);");
				eval("\$this->{$this->_writeOnFile}(\$log, \$level);");
			}
		} else {
			//
			// If it's not ready it triggers an error.
			trigger_error(__CLASS__."::".__FUNCTION__.": Log is not set yet.", E_USER_ERROR);
		}
	}
	//
	// Protected methods.
	/**
	 * This dummy method is used when some kind of writing has to be disabled.
	 */
	protected function dummy($log, $level) {
		// nothing
	}
	/**
	 * This method adds a header message to a log file indicating some basic
	 * properties.
	 */
	protected function header() {
		//
		// Loading global pointers.
		global $wcDefaults;
		//
		// Checking if log files are disabled.
		if(!$wcDefaults["no-logs"]) {
			$aux = str_repeat("-", 80)."\n";
			$aux .= __CLASS__.": Header\n";
			$aux .= "\tDate:  ".date('Y-m-d H:i:s')."\n";
			$aux .= "\tLevel: {$this->level()}\n";
			$aux .= str_repeat("-", 80)."\n";
			file_put_contents($this->path(), $aux, FILE_APPEND);
		}
	}
	/**
	 * This method adds a footer message to a log file indicating some basic
	 * properties.
	 */
	protected function footer() {
		//
		// Loading global pointers.
		global $wcDefaults;
		//
		// Checking if log files are disabled.
		if(!$wcDefaults["no-logs"]) {
			$aux = str_repeat("-", 80)."\n";
			$aux .= __CLASS__.": Footer\n";
			$aux .= "\tDate:  ".date('Y-m-d H:i:s')."\n";
			$aux .= str_repeat("-", 80)."\n";
			file_put_contents($this->path(), $aux, FILE_APPEND);
		}
	}
	/**
	 * This method writes a log message in the log's file.
	 *
	 * @param string $log Message to write in file.
	 * @param int $level Log level for the message to write.
	 */
	protected function realWriteOnFile($log, $level) {
		//
		// Clearing special characters.
		$log = preg_replace("/\033([0-9]*)([;]?)(.*?)m/", "", $log);
		//
		// Writing in the file.
		file_put_contents($this->path(), $log, FILE_APPEND);
	}
	/**
	 * This method prompt a log message on the screen.
	 *
	 * @param string $log Message to prompt on the screen.
	 * @param int $level Log level for the message to show.
	 */
	protected function realWriteOnScreen($log, $level) {
		echo $log;
	}
	/**
	 * This class method allows to access certain log based on its id. If such
	 * log doesn't exist it is created and returned.
	 *
	 * @param string $id Id to look for.
	 * @return WCLog Log associated with the requested id.
	 */
	public static function & Get($id) {
		//
		// Checking if a log associated with the id already exists.
		if(!isset(WCLog::$_Logs[$id])) {
			//
			//  Creating the log.
			WCLog::$_Logs[$id] = new WCLog($id);
		}
		//
		// Returning the log associated with the id.
		return WCLog::$_Logs[$id];
	}
}
?>